*** Sunsoft NES audio engines analysis *** by Matrixz - Sep 2013 Intro ===== This is a document with miscellaneous notes about the sound engines in various Sunsoft games. It doesn't cover data formats, but for each game there is misc. notes, like if there is bankswitching, DPCM usage, and Init/Play (or other) addresses. For some of the games there is info about datas in RAM. (Including current address of BGM datas, which might possibly help for those wanting to hack music). There are comments about comparisons between the different games, and theories about how much the sound programmers re-used or changed code between games. The document is based on notes made per someone's request. General/Common Notes ==================== General, ostensible relation between games' sound engines, ordered chronologically: 1988 - Blaster Master 1989 - Fester's Quest - similar to Blaster Master, some changes in RAM locations, possibly they improved the engine for utilizing DPCM. 1990 - Batman - Similar to Fester's Quest. - Batman (Prototype) - Identical to Batman. 1990 - Journey to Silius - This is either an engine written mostly from scratch, or one based on an earlier game, with many changes. Since I haven't looked into details (such as BGM data or programming), I can't be sure. From analyzing the layout of RAM between the games, there are similarities. All of these games' engines are similar to Journey to Silius: V 1990 - Gremlins 2: the New Batch 1991 - Batman: Return of the Joker (minor difference: utilizes bank-swapping) 1991 - Ufouria: the Saga / Hebereke 1991 - Super Spy Hunter / Battle Formula ^ 1992 - Gimmick! - This is clearly based on the "Journey to Silius" engine (or one of the games that used it), but with added support for Sunsoft 5B chip sound channels in the driver, and other enhancements. It uses bank-swapping for BGM data, and bank-swapping for DPCM samples as well. 1993 - Mr. Gimmick - There are small changes like initialization data for Sunsoft 5B channels being stripped from the BGM data headers, and change in RAM locations. BGM data storage: It appears that all engines used in these games have a loop system for BGM, that allows sections of BGM data (one or more notes and/or commands) to be repeated any number of times. (256 max) Each individual channel has its own set of loop counters. (Including the DPCM channel.) It seems all games allows two layers/levels of loops for all channels. (so you can have a "loop inside a loop"). At least for Journey to Silius and later, those games have a subroutine/macro system, in addition to loops. The BGM data can call an address from the main data, and the BGM data at that address will be played or handled as a subroutine. (The data there can contain notes to be played back and/or commands.) For Journey to Silius and later: The engines are limited so that this can only be done at one level (you can't call a subroutine inside a subroutine.) It looks like the games earlier than Silius, uses a subroutine system as well, which is actually more flexible and uses more memory, but I haven't confirmed it for sure. Batman and Fester's Quest use $6C0-$6FF, and Blaster Master uses memory at $180-$1BF for what looks like a stack for storing the return address for BGM data subroutines/macros, where each channel can grow the stack up to 3 levels deep. (3 bytes used for each level). Common ways that main RAM is organized: In some of the games (earlier than Journey to Silius), memory in $6C0-$6FF is used certain times, possibly being a subroutine stack for all channels. In Blaster Master, this data begins at $180 instead. This is only for Journey to Silius and later: If the engine uses bankswapping for BGMs, the current bank is stored in the very first byte. Next, there are unknown general-purpose datas, in varying size (1 to 4? bytes), which is usually $00. Next, the RAM for the main 4 NES channels (All except DPCM) is placed, consisting of both RAM for BGM and sound effects. This is typically a big chunk of bytes, size somewhere between 64 and 256 bytes. For each type of byte data: One byte for each BGM channel (4), then one byte for each SFX channel. The number of channel slots available for sound effects varies. (Blaster Master has 5, Gimmick has 2, Journey to Silius has 4, etc.) When BGMs are played in a NSF file, in a RAM viewer it's easy to distinct between what RAM is used for BGM and what is SFX, the RAM for BGM will have values higher than $00 and the chunks of RAM in-between that stays $00, is SFX memory in most cases. For Gimmick! only: The channel state memory for the Sunsoft 5B channels are next. The system is the same as with the 4 NES (2A03) channels above, the ordering of types of data as they appear seems to be the same order also. For each type of byte data: One byte for each BGM channel (3), then one byte for each SFX channel (3), 6 altogether. In Mr. Gimmick (Europe), all of this RAM stays $00. Next, is the state memory for the DPCM channel. It seems to follow the same order for different types of data, as with the 4 main channels, but only some types of datas are stored (the DPCM channel doesn't need to store RAM for things like envelopes, volume, etc. that the other channels do.) So, the RAM usage for this channel is significantly smaller. In some of the games, there is spacing for two DPCM channels, so there is a $00 between each DPCM byte used for the BGM. The purpose is unknown, perhaps it's meant for DPCM sound effects. Blaster Master (1988): ====================== Engine is loaded at $8000-$BFFF. RAM usage: Main: Uses memory in the $700-$7FF range, and RAM at $180 and on. Zero Page: $E0-$EF range. Bank system: Single $4000 byte bank loaded at $8000-$BFFF, no bankswap. DPCM samples: None used. Code address: Init: $8013 Play: $8077 RAM details: $71B-$71E : BGM channels - Data Address - Low $724-$727 : BGM channels - Data Address - High $736-$739 : Level 1 Loop Counters $73F-$742 : Level 2 Loop Counters Batman (1989): ============== General: Engine seems similar to Blaster Master, but this game makes use of the DPCM channel, so a difference could be that they added code for playback with that channel. Uses Memory $6C0-$7FF, Zero Page Memory $41-$4F. Engine is loaded at $8000-$BFFF. Init Routine: $8027 Play Routine: $8828 Bank system: Single $4000 byte bank loaded at $8000-$BFFF, no bankswap. DPCM samples: There are 5 presets, including one Kick, one Snare and Toms at three different frequencies. DPCM sample locations (Memory address): $F000-$F0FF: Kick $F100-$F2FF: Snare $F300-$F4FF: Tom (played back at 3 different frequencies.) Note: In my NSF rip, the samples are moved to $C000-$C4FF. RAM details: $72A-$72D: Main 4 BGM channels - Level 1 Loop Counters $731-$734: Main 4 BGM channels - Level 2 Loop Counters $7F3: DPCM channel loop counter Batman (Prototype) (1989): -------------------------- There seems to be no difference to the regular game. In the Batman .NSF (at least the one I have), a range of bytes at Memory Address $89E9 is different, which is data in the DPCM table. But this data is identical between the Batman NES ROM and the prototype ROM. It is because the NSF ripper changed those values, so that the DPCM samples are played from a different address in the NSF rip than in the original NES ROM. Fester's Quest (1989): ====================== Misc.: Engine appears similar to "Batman", going from the RAM layout. But, the programming is not completely the same, it doesn't have a series of JMP instructions at the start of the bank. Init Routine: $803B Play Routine: $8000 Data address: The address table at $BDAB seems to be (looks suspiciously like) the address table for each Sound Id. RAM info: Uses RAM $700-$7FF, and in some cases $6C0-$6FF. Uses Zero Page RAM $80-$93. RAM details: $7F0-$7F1: Pointer to current byte in DPCM channel data. $7F2: DPCM counter down to next DPCM byte. DPCM playback: DPCM playback routine: $8891 DPCM data address table: $BE3D Presets: $00-$23 : Slap Bass Melodic DPCM, from low to high frequency. (except $1F) $1F: Just a very short pop sound.. $24-$34: Melodic DPCM - Orchestra Hit (sounds like a O-Hit sample I've heard in Konami SNES and Arcade games. It's heard in NSF track #2, which I assume is the Title screen song.) Journey to Silius (1990): ========================= General: This engine seems to be a revision from earlier games (Batman or Fester's Quest), and maybe the first game having this revision. I don't know if the BGM data formats, or things like envelopes/instruments works the same way as earlier games, but the ways that RAM is structured have similarities. Code address: Play: $816D Init: $80A5 Reset all: $8009 Bank system: Single $4000 byte bank loaded at $8000-$BFFF that includes the engine code, BGM and SFX data. No bankswapping. RAM layout: Main: $700-$7FF range. RAM details: $70A-$70D: Main 4 BGM channels - Data Address - Low $712-$715: Main 4 BGM channels - Data Address - High $732-$735: Main 4 BGM channels - Subroutine/Macro return Address - Low $73A-$73D: Main 4 BGM channels - Subroutine/Macro return Address - High $7DC: DPCM channel data Low $7DE: DPCM channel data High Gremlins 2: The New Batch (1990): ================================= General: Engine seems very similar to Journey to Silius, except that the main RAM is in the $600-$6FF region. Code addresses is also a bit different (the Play routine is $8176 here but $816D in Silius.) Code address: Play: $8176 Init: $80A5 Reset all: $8009 Data address: $8925-> : Sound Id Address Table RAM layout: Zero Page: Around $80-$90. Main: $600-$6FF region Bank system: Single $4000 byte bank loaded into $8000-$BFFF, no bankswap. RAM details: $60A-$60D: Main 4 BGM channels - Data Address - Low $612-$615: Main 4 BGM channels - Data Address - High $632-$635: Main 4 BGM channels - Subroutine/Macro Return Address - Low $63A-$63D: Main 4 BGM channels - Subroutine/Macro Return Address - High $6DC: DPCM channel data Low $6DE: DPCM channel data High Batman - Return of The Joker (1991): ==================================== General: Engine seems similar to Gremlins 2, the RAM layout is similar but the engine code is a bit different (routine addresses is at different locations from Gremlins 2, also there is an alternate Reset routine.) Code address: Reset all: $800C (JMP at $8000) Alternate reset: $8020 (JMP at $8003) Init: $80B2 (JMP at $8006) Play: $8217 (JMP at $8009) RAM layout: Main: $600-$6FF region. RAM details: $600: ROM bank for the current BGM. $605->: Start of main channel RAM. $60D-$610: Main 4 BGM channels - Data Address - Low $615-$618: Main 4 BGM channels - Data Address - High $6E7: DPCM channel data Low $6E9: DPCM channel data High Hebereke / Uforia: The Saga (1991): =================================== General: The engine seems to be exactly or almost the same as Gremlins 2, only that the main RAM is $100 bytes higher ($700-> instead of $600->). The Init and Play addresses are identical. Bank system: Single $4000 bank loaded at $8000-$BFFF. No bankswap. DPCM samples are at $C000, where the Hard-wired bank is loaded. RAM layout: Main: $700-$7FF region. RAM details: $742-$745: Level 1 loop counters $74A-$74D: Level 2 loop counters Code address: Play: $8176 Init: $80A5 Reset all: $8009 Data address: $895C-> : Sound Id Address Table Battle Formula (Super Spy Hunter) (1991): ========================================= General: Engine seems similar to "Batman - Return of The Joker", except the main RAM is in the $700-$7FF range here. RAM details: $705 stores the current BGM Id for NSF tracks 1, 4 and 7. Maybe something to do with how those are the main musics for the first 3 stages.. $706-$709: Channel Enable/other. (Write 00 to these to easily disable one or more of the channels.) $736-$739: Main 4 BGM channels - Subroutine/Macro return address - Low $73E-$741: Main 4 BGM channels - Subroutine/Macro return address - High $746-$749: Main 4 BGM channels - Level 1 loop counters $7EC: DPCM channel loop counter. Gimmick! (Famicom/Japanese,1992): ================================= Engine/sound datas is loaded into $8000-$BFFF. DPCM samples are loaded at $C000-$DFFF. Init routine JMP instruction is at $8006 (the NSF points to a hacked in routine at $9F30, It reads from a table at $9F60 which translates the Song # to the actual value to pass to $8006 (in Accumulator)). Play routine JMP instruction is at $8009 Memory Usage: Main RAM: $600-$763. Bank system: Engine code is loaded at $8000-$9FFF. BGM banks are swapped at $A000-$BFFF, depending on which BGM is playing. Each $2000 byte BGM bank contains one or more BGM datas (ranging from 3 to 7). No BGM datas spans multiple banks. It seems there is chunks of unused bytes at the end of each bank. At least one of the BGM datas function as a sound effect. Gimmick uses multiple banks for DPCM samples, so the correct bank is swapped in at $C000-$DFFFF depending on which sample is currently being played back. RAM details: $600: ROM bank for the current BGM. $608-$60B: 2A03 Channels BGM - Data Address - Low $60E-$611: 2A03 Channels BGM - Data Address - High $614-$617: NES 2A03 Channel mapping $626-$629: Subroutine return address - Low $62C-$62F: Subroutine return address - High (00 is written when returning from subroutine.) $632-$635: Loop Counters (2A03 channels) - Level 1 $638-$63B: Loop Counters (2A03 channels) - Level 2 $6A4-$6A6: Enable/other registers for Sunsoft 5B channels (write 00 to these to easily disable one or more of the channels.) $6AA-$6AC: Sunsoft 5B BGM - Data Address - Low $6B0-$6B2: Sunsoft 5B BGM - Data Address - High $758: DPCM channel enable $759-$75A: DPCM channel data address $75B: DPCM channel - frame counter until next note/ BGM byte $75F: DPCM channel loop counter $761-$762: Current DPCM table address (Im guessing it changes depending on what kind of instrument is used for melodic or drum DPCM..) Code address: $8131: Initialize channels when a BGM begins $8DD7: DPCM channel playback handler $8E3B: DPCM channel playack - Play back preset in Memory Address $75. (setting a breakpoint here will make the debugger snap just before playing a DPCM sample). DPCM playback: Presets: The DPCM table address changes through the songs, depending on what kinds of instruments are played. But, each table can be used for multiple instruments and samples. I'm not sure yet how it works. (not sure if one type of instrument is exclusive to one exact table address.) Table Address = $BCF5: Preset value: $00-$22: Melodic Slap Bass Table Address = $BDB8: Preset value: $00-$1A: Melodic Bass $1B: Drum sound (kick?) $1C-$1D: Snare $1E-$1F: Snare 2 $20: Kick $21-$22: Conga? $23-$25: Tom? BGM data commands: FB ll hh - Play data at address 'hhll' as subroutine? Mr. Gimmick (Europe): --------------------- RAM details: Differences from Japanese Gimmick: DPCM channel data address is at $753-$754, and the DPCM note frame counter is $755. (The DPCM channel RAM seems to be moved 6 bytes minus relative to the original address.) Other: Although the Sunsoft 5B channel playback is disabled in this version of the game, it appears that the BGM datas for those channels are not removed to conserve space. It's all still in the ROM. However, the initialization data for the Sunsoft 5B channels is removed from each of the BGM datas' headers. (Those are 4 bytes for each channel.) So, the location of BGM datas is slightly different (a few bytes more or less). *** end ***